package com.hc.summary.util;

import android.app.Application;
import android.os.Build;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

public class HotFixUtil {
    static {
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.P){
            try {
                Method forName = Class.class.getDeclaredMethod("forName",String.class);
                Method getDeclaredMethod = Class.class.getDeclaredMethod("getDeclaredMethod", String.class, Class[].class);
                Class<?> vmRuntimeClass= (Class<?>) forName.invoke(null,"dalvik.system.VMRuntime");
                Method getRuntime = (Method) getDeclaredMethod.invoke(vmRuntimeClass, "getRuntime", null);
                Method setHiddenApiExemptions = (Method) getDeclaredMethod.invoke(vmRuntimeClass, "setHiddenApiExemptions", new Class[]{String[].class});
                Object vmRuntime = getRuntime.invoke(null);
                setHiddenApiExemptions.invoke(vmRuntime,new Object[]{new String[]{"L"}});
            }catch (Throwable e){
                e.printStackTrace();
            }
        }
    }
    public static void loadPatchDex(Application application, File patchDex){
        ClassLoader classLoader = application.getClassLoader();
        try {
            Field pathListField = findField(classLoader, "pathList");//1
            Object pathList = pathListField.get(classLoader);
            if(pathList != null){
                Field dexElementsField = findField(pathList, "dexElements");//2
                Object[] dexElements = (Object[]) dexElementsField.get(pathList);
                ArrayList<IOException> suppressedExceptions = new ArrayList<>();
                List<File> files = new ArrayList<>();
                files.add(patchDex);
                File cacheDir = application.getFilesDir();
                Method makeDexElementsMethod = findMethod(pathList,"makeDexElements",List.class,File.class,List.class,ClassLoader.class,boolean.class);//3
                Object[] patchElements = (Object[]) makeDexElementsMethod.invoke(null,files,cacheDir,suppressedExceptions,classLoader,false);
                Object[] newElements = (Object[]) Array.newInstance(patchElements.getClass().getComponentType(),patchElements.length+dexElements.length);
                System.arraycopy(patchElements,0,newElements,0,patchElements.length);
                System.arraycopy(dexElements,0,newElements,patchElements.length,dexElements.length);
                dexElementsField.set(pathList,newElements);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    public static Field findField(Object object,String name){
        Class<?> cls = object.getClass();
        while (cls != Object.class) {
            try {
                Field field = cls.getDeclaredField(name);
                field.setAccessible(true);
                return field;
            }catch (Exception e){
                e.printStackTrace();
            }
            cls = cls.getSuperclass();
        }
        return null;
    }

    public static Method findMethod(Object object, String name,Class<?>...parameterTypes){
        Class<?> cls = object.getClass();
        while (cls != Object.class) {
            try {
                Method method = cls.getDeclaredMethod(name,parameterTypes);
                method.setAccessible(true);
                return method;
            }catch (Exception e){
                e.printStackTrace();
            }
            cls = cls.getSuperclass();
        }
        return null;
    }
}
